use std::fmt::{Debug,Display};
use serde::{Deserialize, Serialize};

trait HasArea {
    fn area(&self) -> f64;
}

struct Circle {
    x : f64,
    y : f64,
    radius : f64,
}

impl HasArea for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

struct Triangle {
    base : f64,
    height : f64,
}

impl HasArea for Triangle {
    fn area(&self) -> f64 {
        0.5*(self.base*self.height)
    }
}

//trait与具体类型
fn test_trait_impl() {
    let c = Circle {
        x: 0.0f64,
        y: 0.0f64,
        radius: 0.1f64,
    };

    println!("circle c has the area of {}", c.area());
}

//trait对象
fn test_trait_obj() {
    trait Foo { fn method(&self) -> String; }

    impl Foo for u8 { fn method(&self) -> String { format!("u8: {}", *self) } }
    impl Foo for String { fn method(&self) -> String { format!("string: {}", *self) } }

    fn do_something<T: Foo>(x: &T) {
        println!("{}", x.method());
    }

    let x = "Hello".to_string();
    do_something(&x);
    let y = 8u8;
    do_something(&y);
}

//trait当作参数
fn test_trait_inparam() {
    fn calculate_area(item : impl HasArea) {
        println!("circle c has the area of {}", item.area());
    }

    let c = Circle {
        x: 0.0f64,
        y: 0.0f64,
        radius: 0.1f64,
    };

    calculate_area(c);
}

// trait 与泛型
fn test_trait_generic() {
    fn calculate_area<T>(item : T)
        where T: HasArea {
        println!("circle c has the area of {}", item.area());
    }

    let c = Circle {
        x: 0.0f64,
        y: 0.0f64,
        radius: 0.1f64,
    };

    let a = Triangle{base: 10.5, height: 17.4};
    calculate_area(c);
    calculate_area(a);
}
//多个trait 约束，使用 + 运算符
fn test_trait_multiple() {
    fn compare_prints<T>(t : &T)
        where T: Debug+Display {
        println!("Debug output {:?}", t);
        println!("Display {}", t);
    }

    let string = "Yiqihai";
    compare_prints(&string);
}

//继承
fn test_derive() {
    trait Foo {
        fn foo(&self);
    }

    trait FooBar : Foo {
        fn foobar(&self);
    }

    struct Baz;

    impl Foo for Baz {
        fn foo(&self) {
            println!("foo");
        }
    }

    impl FooBar for Baz {
        fn foobar(&self) {
            println!("foobar");
        }
    }

    let s = Baz;
    s.foo();
    s.foobar();
}

fn test_clone_move() {
    #[derive(Debug)]
    struct Fool {
        a: i32,
        b: bool,
    }

    impl Copy for Fool {}
    impl Clone for Fool {
        fn clone(&self) -> Fool {
            Fool {a: self.a, b: self.b}
        }
    }

    let x = Fool {a: 100, b: true};
    let mut y = x;
    y.b = false;

    println!("{:?}", x);
    println!("{:?}", y);
}

fn test_serialize() {
    #[derive(Serialize, Deserialize, Debug)]
    struct A {
        x: isize,
        s: String,
        y: usize,
    }

    let a = A {
        x: -4,
        s: "foo".to_string(),
        y: 10,
    };

    // Or just to a buffer
    let bytes = bincode::serialize(&a).unwrap();
    println!("{:?}", bytes);

    let decoded : A = bincode::deserialize(&bytes[..]).unwrap();
    print!("{:?}", decoded);
}

fn main() {
//    test_trait_impl();
//    test_trait_inparam();
//    test_trait_generic();
//    test_trait_multiple();
//    test_derive();
//    test_trait_obj();
//    test_clone_move();
    test_serialize();

}
